package com.naiterui.ehp.bs.doctor.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.im.PatientMsgSendParamBO;
import com.naiterui.ehp.bp.bo.patient.PatientInquirerBO;
import com.naiterui.ehp.bp.bo.statistics.AsConsultRecordBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.CommonConstant.ConsultType;
import com.naiterui.ehp.bp.constants.ImConstants;
import com.naiterui.ehp.bp.domain.ConsultBill;
import com.naiterui.ehp.bp.domain.ConsultSession;
import com.naiterui.ehp.bp.domain.Doctor;
import com.naiterui.ehp.bp.domain.DoctorCharge;
import com.naiterui.ehp.bp.domain.DrMessageConsultOrder;
import com.naiterui.ehp.bp.domain.Patient;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.thread.ThreadPoolUtil;
import com.naiterui.ehp.bs.doctor.dao.IConsultBillDao;
import com.naiterui.ehp.bs.doctor.dao.IConsultSessionDao;
import com.naiterui.ehp.bs.doctor.dao.IDoctorChargeDao;
import com.naiterui.ehp.bs.doctor.dao.IDoctorDao;
import com.naiterui.ehp.bs.doctor.dao.IPatientDao;
import com.naiterui.ehp.bs.doctor.dao.mongo.AdMessageDao;
import com.naiterui.ehp.bs.doctor.exception.ExceptionCodes;
import com.naiterui.ehp.bs.doctor.fegin.ImFeginClient;
import com.naiterui.ehp.bs.doctor.fegin.PatientFeignClient;
import com.naiterui.ehp.bs.doctor.fegin.SearchFeignClient;
import com.naiterui.ehp.bs.doctor.prodocl.Message;
import com.naiterui.ehp.bs.doctor.remote.IAdRemoter;
import com.naiterui.ehp.bs.doctor.repository.IConsultSessionRepository;
import com.naiterui.ehp.bs.doctor.service.IConsultSessionService;
import com.naiterui.ehp.bs.doctor.service.IDrFollowUpRecordService;
import com.naiterui.ehp.bs.doctor.service.IDrMessageConsultOrderService;
import com.naiterui.ehp.bs.doctor.utils.RedisUtils;
import com.naiterui.ehp.bs.doctor.vo.ConsultCostVO;
import com.naiterui.ehp.bs.doctor.vo.DialogueDisplayVO;
import com.naiterui.ehp.bs.doctor.vo.SessionParamVO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class AdConsultSessionServiceImpl implements IConsultSessionService {

    /**
     * 日志对象：<code>logger</code>
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AdConsultSessionServiceImpl.class);

    @Autowired
    private IDoctorChargeDao doctorChargeDao;
    @Autowired
    private IConsultSessionDao consultSessionDao;

    @Autowired
    private IConsultSessionRepository consultSessionRepository;

    @Autowired
    private IConsultBillDao consultBillDao;
    @Autowired
    private IDoctorDao doctorDao;
    @Autowired
    private IPatientDao patientDao;
    @Autowired
    private IDrMessageConsultOrderService drMessageConsultOrderService;
    @Autowired
    private PatientFeignClient patientFeignClient;
    @Autowired
    private AdMessageDao messageDao;
    @Autowired
    private IAdRemoter remoter;
    @Autowired
    private ImFeginClient imFeginClient;
    @Autowired
    private IDrFollowUpRecordService drFollowUpRecordService;
    @Autowired
    private SearchFeignClient searchFeignClient;

    /*
     * IM聊天创建会话 IConsultSessionService.addNewSession()
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> addNewSession(SessionParamVO paramVO) throws BusinessException {

        /*
         * 解析fromId、toId
         */
        Long patientId = null;
        Long doctorId = null;
        boolean isDocStart = false;

        if (paramVO.getRelation() == 0) {
            // 患者发起
            patientId = paramVO.getFromId();
            doctorId = paramVO.getToId();
        } else if (paramVO.getRelation() == 1) {
            // 医生发起
            patientId = paramVO.getToId();
            doctorId = paramVO.getFromId();
            isDocStart = true;
        } else {
            LOGGER.error("= addNewSession ERROR ; illegal IM relation : relation {}", paramVO.getRelation());
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }

        /*
         * 构建收费信息
         */
        Integer costType = 0;
        Integer cost = 0;
        String costCache = RedisUtil.valueOps().getString(CommonConstant.PATIENT_PERSONAL_DOCTOR_FIRST_BIND + doctorId + "_" + patientId);
        if (StringUtils.isNotBlank(costCache)) {
            cost = Integer.parseInt(costCache);
        } else if (!isDocStart) {
            // 医生发起默认免费,非医生发起获取相关设置(1：图文；2：电话；4：视频)
            DoctorCharge doctorCharge = doctorChargeDao.getDoctorChargeByType(doctorId, 1);
            if (null != doctorCharge && null != doctorCharge.getCharge() && doctorCharge.getCharge() > 0) {
                costType = 1;
                cost = doctorCharge.getCharge();
            }
        }

        /*
         * 创建mysql端session记录
         */
        ConsultSession session = new ConsultSession();
        session.setCost(cost);
        session.setCostType(costType);
        session.setDoctorId(doctorId);
        session.setPatientId(patientId);
        session.setInquirerId(paramVO.getInquirerId());
        session.setRelation(paramVO.getRelation());
        session.setRecomDrugFlag(0);
        session.setSessionId(paramVO.getSessionId());
        session.setStartTime(new Date(paramVO.getBeginTime()));
        session.setCommnetStatus(ConsultSession.HAVE_NOT_COMMENT);
        session.setSessionStatus(ConsultSession.SESSION_STATUS_MISSED);
        // session.setEndTime(new Date(paramVO.getEndTime()));

        consultSessionDao.save(session);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("cost", cost);
        resultMap.put("costType", costType);
        resultMap.put("consultSessionId", session.getId());
        handleVisit(doctorId, patientId, paramVO.getBeginTime());
        return resultMap;
    }

    private void handleVisit(Long doctorId, Long patientId, Long beginTime) {
        ThreadPoolUtil.execute(() -> {
            Date consultTime = new Date(beginTime);
            try {
                drFollowUpRecordService.updateVisitCompleteStatus(doctorId, patientId, consultTime, CommonConstant.ConsultType.GRAPHIC, "", 0L);
            } catch (BusinessException e) {
                LOGGER.error("图文问诊处理随访任务异常：{}", e);
            }
        });
    }

    /*
     * IM医生主动结束咨询会话+超时自动结束会话 IConsultSessionService.updateSessionForClose()
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> updateSessionForClose(String sessionId, Long endTime, Integer freeFlag, Boolean isManual, Boolean isDeletePatient)
            throws BusinessException {

        ConsultSession session = consultSessionDao.getBySessionId(sessionId);
        if (null == session) {
            LOGGER.warn("- updateSessionForClose PARAM ERROR ; session not exist : IMsessionId {},endTime {},freeFlag {}", sessionId,
                    endTime, freeFlag);
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        if (null != session.getEndTime()) {
            // 已结束会话处理
            LOGGER.warn("- updateSessionForClose PARAM ERROR ; session already closed: IMsessionId {},endTime {},freeFlag {}", sessionId,
                    endTime, freeFlag);
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }

        Long doctorId = session.getDoctorId();
        Long patientId = session.getPatientId();

        /*
         * 更新ConsultSession bill相关信息
         */
        session.setBillId(-1L);
        session.setEndTime(new Date(endTime));
        session.setSessionStatus(ConsultSession.SESSION_STATUS_OVER);
        // 如果不是手动结束会话，判断医生有没有回复消息
        if (!isManual) {
            Message firstSessionMessage = messageDao.getFirstSessionMessage(sessionId, doctorId, patientId);
            // 如果没有医生像患者发送过消息，将会话状态修改为未回复
            if (firstSessionMessage == null) {
                session.setSessionStatus(ConsultSession.SESSION_STATUS_UNANSWERED);
            }
        }
        consultSessionDao.update(session);

        /*
         * V2.7.0 会话结束，变更图文咨询订单会话状态：已结束
         */
        drMessageConsultOrderService.updateMessageConsultOrderSessionStatusBySessionId(sessionId, DrMessageConsultOrder.CONSULT_SESSION_STATUS_END);
        RedisUtils.delete(CommonConstant.PT_MESSAGE_CONSULT + patientId + "_" + doctorId);
        RedisUtils.delete(CommonConstant.PT_MESSAGE_CONSULT + patientId + "_" + doctorId + "_" + "flag");
        /*
         * 账单生成成功，异步触发患者端支付提醒等相关操作
         */
        Doctor doctor = doctorDao.get(doctorId);
        Patient patient = patientDao.get(patientId);
        if (null != doctor && null != patient) {
            String content;
            if (isDeletePatient) {
                content = "本次会话已结束，如有问题没有咨询完成，可再次向医生发起咨询~";
            } else {
                content = "与" + doctor.getName() + "医生的问诊已结束。如有需要请再次向医生咨询~";
            }
            // 发送结束咨询提醒
            PatientMsgSendParamBO patientMsgSendParamBO = new PatientMsgSendParamBO();
            patientMsgSendParamBO.setMsgType(ImConstants.MESSAGE_TYPE_PATIENTS_CONSULT_FINISH);
            patientMsgSendParamBO.setDoctorId(doctorId);
            patientMsgSendParamBO.setPatientId(patientId);
            patientMsgSendParamBO.setInquirerId(session.getInquirerId());
            patientMsgSendParamBO.setContent("本次咨询结束，如有疑问可再次咨询");
            imFeginClient.sendPatientSysMessage(patientMsgSendParamBO);

            // 发送服务评价通知
            JSONObject message = new JSONObject();
            JSONObject from = new JSONObject();
            from.put("id", doctorId);
            message.put("from", from);
            JSONObject contentJson = new JSONObject();
            contentJson.put("consultSessionId", session.getId());
            contentJson.put("consultType", ConsultType.GRAPHIC);
            contentJson.put("content", content);
            message.put("content", contentJson);
            message.put("sendTime", System.currentTimeMillis());
            JSONObject sessionJson = new JSONObject();
            sessionJson.put("sessionId", session.getSessionId());
            message.put("session", sessionJson);
            message.put("type", "10006");
            JSONObject to = new JSONObject();
            to.put("id", patientId);
            message.put("to", to);
            message.put("lastPlatform", 0);
            LOGGER.info("医生结束会话发送消息,param: {}", message.toJSONString());
            imFeginClient.sendServiceEvaluation(message.toJSONString());

            // 异步同步问诊数据到统计
            ThreadPoolUtil.execute(() -> {
                AsConsultRecordBO consultRecordBO = new AsConsultRecordBO();
                consultRecordBO.setDoctorId(doctorId);
                consultRecordBO.setPatientId(patientId);
                consultRecordBO.setConsultType(AsConsultRecordBO.TYPE_CONSULT);
                consultRecordBO.setConsultId(session.getId());
                consultRecordBO.setConsultPrice(session.getCost());
                consultRecordBO.setGenerateDate(new Date());
                consultRecordBO.setCreatedAt(new Date());
                consultRecordBO.setCreatedBy("system");
                searchFeignClient.createConsultRecordIndex(consultRecordBO);
            });

        } else {
            LOGGER.error("结束回话生成账单异常：医生或患者不存在：doctorId {},patientId {}", doctorId, patientId);
        }


        // 构建返回结果：session对应账单支付状态 change by Gaoll 2016年3月7日
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("payStatus", ConsultBill.PAY_STATUS_NO_PAID);
        resultMap.put("consultId", session.getId());

        // 异步执行监管同步
        ThreadPoolUtil.execute(() -> {
            try {
                LOGGER.info("异步同步监管业务数据，sessionId {}", sessionId);
                this.remoter.supervisePushSession(sessionId);
            } catch (BusinessException e) {
                LOGGER.error("会话 {} 同步监管业务异常，异常信息 {}", sessionId, e);
            } catch (Exception e) {
                LOGGER.error("会话 {} 同步监管系统异常，异常信息 {}", sessionId, e);
            }

        });

        return resultMap;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSessionRecomFlag(Long consultSessionId) {
        consultSessionDao.updateSessionRecomFlag(consultSessionId);
    }

    @Override
    public List<Long> findInqurirerByPatientIdAndDoctor(Long patientId, Long doctorId) throws BusinessException {

        return consultSessionRepository.findByPatientIdAndDoctorId(patientId, doctorId);
    }

    @Override
    public Long findInquirerBySessionId(String sessionId) throws BusinessException {
        List<ConsultSession> sessions = consultSessionRepository.findBySessionId(sessionId);
        if (CollectionUtils.isEmpty(sessions)) {
            throw new BusinessException(ExceptionCodes.CONSULT_NOT_EXIST);
        }
        return sessions.get(0).getInquirerId();
    }

    @Override
    public Long findInquirerById(Long id) throws BusinessException {
        ConsultSession session = consultSessionRepository.get(id);
        if (session == null) {
            throw new BusinessException(ExceptionCodes.CONSULT_NOT_EXIST);
        }
        return session.getInquirerId();
    }

    @Override
    public Map<Long, Long> findInquirerByIds(Collection<Long> ids) throws BusinessException {
        Iterable<ConsultSession> iterable = consultSessionRepository.findAllById(ids);
        Map<Long, Long> result = new HashMap<>();
        iterable.forEach(s -> {
            result.put(s.getId(), s.getInquirerId());
        });
        return result;
    }

    @Override
    public ConsultCostVO findConsultCostByDoctorIdAndPatientIdAndInquirerId(Long doctorId, Long patientId, Long inquirerId, Integer chargeType) throws BusinessException {
        ConsultSession consultSession = consultSessionDao.findConsultSessionByDoctorIdAndPatientIdAndInquirerId(doctorId, patientId, inquirerId);
        if (null != consultSession) {
            return ConsultCostVO.builder().doctorId(doctorId).costType(chargeType).cost(consultSession.getCost()).build();
        }
        DoctorCharge doctorCharge = doctorChargeDao.findFirstByDoctorIdAndChargeType(doctorId, chargeType);
        if (null == doctorCharge) {
            throw new BusinessException(ExceptionCodes.CHARGE_CONSULT_DOCTOR_NOT_EXISTS);
        }
        return ConsultCostVO.builder().doctorId(doctorId).costType(chargeType).cost(doctorCharge.getCharge()).build();
    }

    @Override
    public DialogueDisplayVO dialogueDisplay(Long doctorId, Long patientId, Long inquirerId) {
        DialogueDisplayVO dialogueDisplayVO = new DialogueDisplayVO();
        boolean sessionKey = RedisUtils.existsKey(doctorId + "_" + patientId);
        // 1.1.1需求医生主动发消息和首次绑定赠送的可以发送病历、处方 2021-06-15
        dialogueDisplayVO.setIsSendCase(true);
        dialogueDisplayVO.setIsSendPrescription(true);
        dialogueDisplayVO.setIsCreateFollowUp(true);
        if (sessionKey) {
            dialogueDisplayVO.setIsEndButton(true);
        }
        boolean bindKey = RedisUtils.existsKey(CommonConstant.PATIENT_PERSONAL_DOCTOR_FIRST_BIND + doctorId + "_" + patientId);
        if (bindKey) {
            dialogueDisplayVO.setIsEndButton(false);
        }
        if (null != inquirerId) {
            PatientInquirerBO inquirerBO = patientFeignClient.getInquirerInfoById(inquirerId);
            if ((inquirerBO.getChildTag() == 1 && null == inquirerBO.getGuardianIdCard())
                    || (inquirerBO.getChildTag() != 1 && null == inquirerBO.getIdCard())) {
                dialogueDisplayVO.setIsSendCase(false);
                dialogueDisplayVO.setIsSendPrescription(false);
                dialogueDisplayVO.setIsCreateFollowUp(false);
            }
        }
        return dialogueDisplayVO;
    }

    @Override
    public void updateSessionStatus(String sessionId, Integer sessionStatus) throws BusinessException {
        List<ConsultSession> sessions = consultSessionRepository.findBySessionId(sessionId);
        if (CollectionUtils.isEmpty(sessions)) {
            throw new BusinessException(ExceptionCodes.CONSULT_NOT_EXIST);
        }
        ConsultSession consultSession = sessions.get(0);
        consultSession.setSessionStatus(sessionStatus);
        consultSessionRepository.update(consultSession);
    }

    @Override
    public Long findIdBySessionId(String sessionId) throws BusinessException {
        List<ConsultSession> sessions = consultSessionRepository.findBySessionId(sessionId);
        if (CollectionUtils.isEmpty(sessions)) {
            throw new BusinessException(ExceptionCodes.CONSULT_NOT_EXIST);
        }
        return sessions.get(0).getId();
    }
}
